package go_timewheel

import (
	"container/list"
	"sync"
	"time"
)

type scheduleTask struct {
	task        func() // 任务函数，表示要执行的任务
	pos         int    // 任务的位置 slot槽位中的位置
	key         string // 任务的键值
	cycle       int    // 任务的周期
	isCycleTask bool   // 是否为周期任务
}

type TimeWheel struct {
	sync.Once
	interval       time.Duration            // 时间轮的间隔
	stop           chan struct{}            // 停止时间轮的通道
	addTaskChan    chan *scheduleTask       // 时间轮的任务通道
	removeTaskChan chan string              // 时间轮的移除任务通道
	slots          []*list.List             // 时间轮的槽
	pointer        int                      // 时间轮的指针
	ticker         *time.Ticker             // 时间轮的定时器
	keyToETask     map[string]*list.Element // 任务的key和任务的映射关系
}

// NewTimeWheel 创建时间轮
func NewTimeWheel(slotNum int, interval time.Duration) *TimeWheel {
	if slotNum <= 0 {
		slotNum = 10
	}

	if interval <= 0 {
		interval = time.Second
	}

	t := TimeWheel{
		interval:       interval,
		stop:           make(chan struct{}),
		keyToETask:     make(map[string]*list.Element),
		addTaskChan:    make(chan *scheduleTask),
		removeTaskChan: make(chan string),
		slots:          make([]*list.List, 0, slotNum),
		ticker:         time.NewTicker(interval),
	}

	for i := 0; i < slotNum; i++ {
		t.slots = append(t.slots, list.New())
	}
	go t.start()
	return &t
}

// start 启动时间轮
func (t *TimeWheel) start() {
	// 1. 异常兜底 (关闭定时器)
	defer func() {
		if err := recover(); err != nil {
		}
	}()

	// 2. 判断任务类型分别执行
	for {
		select {
		case <-t.stop:
			return
		case <-t.ticker.C: // 定时器触发
			t.tick()
		case task := <-t.addTaskChan:
			t.addTask(task)
		case key := <-t.removeTaskChan:
			t.removeTask(key)
		}
	}
}

// Stop 停止时间轮
func (t *TimeWheel) Stop() {
	t.Do(func() {
		t.ticker.Stop()
		close(t.stop)
	})
}

// 添加任务
func (t *TimeWheel) addTask(task *scheduleTask) {
	taskList := t.slots[task.pos]
	if _, ok := t.keyToETask[task.key]; ok {
		t.removeTask(task.key)
	}
	eTask := taskList.PushBack(task)
	t.keyToETask[task.key] = eTask
}

// 删除任务
func (t *TimeWheel) removeTask(key string) {
	eTask, ok := t.keyToETask[key]
	if !ok {
		return
	}
	// 删除任务节点
	delete(t.keyToETask, key)
	task, _ := eTask.Value.(*scheduleTask)
	_ = t.slots[task.pos].Remove(eTask)
}

func (t *TimeWheel) tick() {
	taskList := t.slots[t.pointer]
	defer func() {
		t.pointer = (t.pointer + 1) % len(t.slots)
	}()
	t.execute(taskList)
}

func (t *TimeWheel) execute(taskList *list.List) {
	for e := taskList.Front(); e != nil; {
		// 获取任务节点
		eTask := e.Value.(*scheduleTask)
		if eTask.cycle > 0 {
			eTask.cycle--
			e = e.Next()
			continue
		}

		// 执行任务
		go func() {
			defer func() {
				if err := recover(); err != nil {
					t.removeTask(eTask.key)
				}
			}()
			eTask.task()
		}()

		// 移除已完成任务
		next := e.Next()
		taskList.Remove(e)
		delete(t.keyToETask, eTask.key)
		e = next
	}
}

// AddTask 外部添加任务
func (t *TimeWheel) AddTask(key string, task func(), executeAt time.Time) {
	pos, cycle := t.getPosAndCircle(executeAt)
	t.addTaskChan <- &scheduleTask{
		pos:   pos,
		cycle: cycle,
		task:  task,
		key:   key,
	}
}

// RemoveTask 删除任务
func (t *TimeWheel) RemoveTask(key string) {
	t.removeTaskChan <- key
}

func (t *TimeWheel) getPosAndCircle(executeAt time.Time) (int, int) {
	delay := int(time.Until(executeAt))
	cycle := delay / (len(t.slots) * int(t.interval))
	pos := (t.pointer + delay/int(t.interval)) % len(t.slots)
	return pos, cycle
}
